<?php
/*--------------------------------------------------------------------------------------------------
class templateFeederMagic

string feed($template,$source_array):
injects values from a multidimensionnal, associative array into a template string
works by searching commands like __command__ and processes them

supported commands :
__A_CONSTANT__
replaces allcaps+underscore string by the constant if defined, else deletes it

__{path_to_data}__
searches for corresponding data in $array_source and dumps its value, else deletes it. See arraypath for {path} format.

__if{path}#{id}__ , if-notnull{path}#{id}__
processes the block until __end#{id}__ if value exists and is not null (or false)

__if{path}equal{path2}#{id}__ , __if{path}equal({value})#{id}__

switch

for-in
n >> [template][id]

foreach


injection de données dans un modèle
repère des structures de la forme __(commande)__ et opère les remplacements demandés


necessite la classe arrayPath

http://code.fredbird.org
file version 2005-05-20 16:47:40
--------------------------------------------------------------------------------------------------*/

class templateFeederMagic {
/*----------------------------------------------------------------------------------------------
                                                                           parametres par defaut
----------------------------------------------------------------------------------------------*/
	var $default_parameters=array();
	var $data=array(); // données de travail
	/* attention ! le nombre de parenthèses de ereg_command a un impact sur la creation d'autres regex
	voir feedblock(), getblock() etc
	*/
	//var $ereg_command='[][)(~a-zA-Z0-9.,/@+*_-]+';
	//var $ereg_command='([^_]|(._.))+';
	//var $ereg_chars_command="[][)(~a-zA-Z0-9.,/@+*_-]";
	//var $ereg_command='[^_ #]+(_?[^_ #]+)?'; // autorise au plus un _ isolé au sein de la commande, et ni espace ni #
	var $ereg_command='[^_ #]+(_?[^_ #]+)*'; // autorise au plus un _ consécutif au sein de la commande, et ni espace ni #
	var $ereg_constant='[A-Z]+[A-Z_]*';
	//var $ereg_static_value='[:alnum:]+';
	var $ereg_static_value='[a-zA-Z0-9]+';
	//var $ereg_chars=
	var $ereg_id='#[a-zA-Z0-9]+';
/*----------------------------------------------------------------------------------------------
                                                                           constructeur eventuel
----------------------------------------------------------------------------------------------*/
	/*function kap_(& $controller) {
		// appel du constructeur parent
		$const=get_parent_class($this);
		parent::$const($controller);
		// suite
	}*/
/*----------------------------------------------------------------------------------------------
                                                                              methodes publiques
a lier dans components/controller.kap-core.php et api.kap-core.php
----------------------------------------------------------------------------------------------*/
	// injecte les données de data dans la chaine template
	function feed($template,$data=array()) {
		if (!is_string($template)) {
			trigger_error("templatefeedermagic: template is not typed string but typed ".gettype($template),E_USER_NOTICE);
			print_r($template);
			return false;
		}
		//echo "templatefeeder: feeding ".affvar($template,'template')." with ".affvar($data,'data');
		// le template est il un fichier ?
		if (is_file($template))
			$template=join('',file($template));
		$this->data=&new arraypath();
		$this->data->loadData($data);
		$this->data->setSyntax('brackets');
		// construction d'expressions régulières
		$this->ereg_path=$this->data->makePathEreg();// echo "ereg_path = ".$this->ereg_path;
		/*$this->ereg_commands='('.$this->ereg_constant.')'
		.'|('.$this->data->makePathEreg().')';*/
		return $this->feedBlock($template);
	}
/*----------------------------------------------------------------------------------------------
                                                                                méthodes privées
----------------------------------------------------------------------------------------------*/
	// traite contextuellement un bloc du modele
	function & feedBlock(& $template,$context='') {
		// indication du contexte
		//echo "traitement du contexte $context";
		$this->data->setContext($context);
		// recherche de la prochaine commande
		//$ereg='(__'.$this->ereg_commands.'(#'.$this->ereg_id.')?__)'; $n=0;
		//$ereg='__([^ #]+)(#'.$this->ereg_id.')?__'; $n=0;
		$ereg='__('.$this->ereg_command.')('.$this->ereg_id.')?__'; $n=0;
		//echo affvar($ereg,'ereg commande',false);
		//echo "<p>treating block ".affvar($template)."<p>";
		while (ereg($ereg,$template,$found)) {
			//echo "<p>".affvar($found,'found_command',false)."<p>";
			$command=$found[1];
			if (ereg('('.$this->ereg_id.')',$found[0],$found2))
				$id=$found2[1];
			else $id=NULL;
			//echo affvar($command,'command',false)." id $id";
			//echo "<p>templateFeederMagic: treating command ".$found[1].", id ".$found[2]."</p>";
			if (!$this->treatCommand($template,$command,$id,$context)) {
				//$template=ereg_replace($found[0],'',$template);
				break;
			}
			// suppression de la commande ?
			$n++; if ($n>20) break;
		}
		return $template;
	}
	// analyse et traitement des commandes
	function & treatCommand(&$template, $command,$id,$context) {
		//echo "<p>treating command $command (id $id)</p>";
		//echo $this->ereg_path;
		switch (true) {
		// constantes
		case ereg('^'.$this->ereg_constant.'$',$command,$found):
			//echo affvar($found);
			return $this->replaceConstantValue($template,$found[0]);
			break;
		// blocs if
		case ereg('^if(-notnull)?('.$this->ereg_path.')$',$command,$found):// echo "if-notnull";
			return $this->ifNotNull($template,$found[2],$command,$id,$context);
			break;
		case ereg('^if('.$this->ereg_path.')equal',$command,$found)
		and ereg('equal(.*)$',$command,$found2):// echo 'if-equal';
			//echo affvar($found2);
			return $this->ifEqual($template,$found[1],$found2[1],$id,$context);
			break;
		// switch
		case ereg('^switch(.*)$',$command,$found):
			return $this->switchCase($template,$found[1],$id,$context);
			break;
		// for
		case ereg('^for-in\(([-]?[0-9.]+),([-]?[0-9.]+),([-]?[0-9.]+)\)',$command,$found):
			return $this->for_in($template,$found[1],$found[2],$found[3],$id,$context);
			break;
		// foreach
		case ereg('^foreach('.$this->ereg_path.')$',$command,$found):
			return $this->for_each($template,$found[1],$id,$context);
			break;
		// valeurs variables
		case ereg('^'.$this->ereg_path.'$',$command,$found):
			$path=$command;
			//echo "<p>=> value replacement for $path";
			return $this->replacePathValue($template,$path);
			break;
		default:
			trigger_error("templateFeederMagic : unsupported command $command encountered",E_USER_NOTICE);
			return false;
		}
	}
	// traitement des remplacement de constantes
	function & replaceConstantValue(& $template, $constant) {
		//echo "treating $constant";
		if (defined($constant)) {
			/*if (!$value=eval("return $constant;"))
				return false;*/
			$value=strval(eval("return $constant;"));
			if (!$template=ereg_replace('__'.$constant.'__',$value,$template))
				return false;
		} else {
			trigger_error("kap_templateFeeder: undefined constant $constant",E_USER_NOTICE);
			if (!$template=ereg_replace('__'.$constant.'__','',$template))
				return false;
		}
		return true;
	}
	// traitement des remplacements de chemin par la valeur
	function & replacePathValue(& $template, $path) {
		$value=$this->data->get($path);
		//echo "<p>getting value for $path : ".$value." with context ".$this->data->getContext()."</p>";
		//if ($value=) $value='';// echo affvar($value);
		if (is_array($value)) $value="Array!";
		$value=strval($value);
		//echo $path."=".affvar($value);
		// remplacement
		$path=$this->escapeEregChars($path);
		//echo "$path => $value";
		if (!$template=ereg_replace('__'.$path.'__',$value,$template)) {
			trigger_error("templateFeederMagic: failed to replace __{$path}__ by $value in template",E_USER_NOTICE);
			return false;
		}
		return true;
	}
	// traitement des if-notnull (ou if[path])
	function & ifNotNull(& $template,$path,$command,$id,$context) {
		// path est le chemin de référence
		$context.=$path;
		$value=$this->data->get($path);// echo affvar($value);
		// suppression du bloc
		if ($value===NULL) return $this->replaceBlock($template,$id);
		// traitement du sous-bloc et substitution
		$block=$this->getBlock($template,$id);
		$block=$this->feedBlock($block,$context);
		return $this->replaceBlock($template,$id,$block);
	}
	// traitement des if[path]equal
	function & ifEqual(& $template,$operand1,$operand2,$id,$context) {
		// operand1 est le chemin de réference
		$context.=$operand1;
		// recuperation de la valeur des operandes
		$value1=$this->getStrVal($operand1);
		$value2=$this->getStrVal($operand2);
		// comparaison
		//echo "$value1==$value2";
		if ($value1==$value2) {
			// traitement du sous-bloc et substitution
			$block=$this->getBlock($template,$id);
			$block=$this->feedBlock($block,$context);
			return $this->replaceBlock($template,$id,$block);
		} else {
			// suppression du bloc
			return $this->replaceBlock($template,$id);
		}
	}
	// traitement des switch
	function & switchCase(& $template,$operand1,$id,$context) {
		// operand1 est le chemin de réference
		$context.=$operand1;
		// evaluation de l'operande 1
		$value1=$this->getStrVal($operand1);
		// extraction du bloc
		$block=$this->getBlock($template,$id);
		// recherche des case / default
		$ereg='__case(('.$this->ereg_path.')|(\('.$this->ereg_static_value.'\)))('.$this->ereg_id.')__';
		//echo $ereg;
		while (ereg($ereg,$block,$found)) {
			$idcase=$found[8]; // attention au cas ou l'ereg de path change !
			//echo affvar($found); break;
			// evaluation de l'operande 2
			$value2=$this->getStrVal($found[1]);
			if ($value1==$value2) {
				// extraction du sous-bloc, traitement et substitution
				$newblock=$this->getBlock($block,$idcase);
				$newblock=$this->feedblock($newblock,$context);
				return $this->replaceBlock($template,$id,$newblock);
			} else {
				// suppression du sous-bloc
				$this->replaceBlock($block,$idcase);
			}
		}
		// recherche d'un "default"
		if (ereg('__default('.$this->ereg_id.')__',$block,$found)) {
			$idcase=$found[1];
			$newblock=$this->getBlock($block,$idcase);
			$newblock=$this->feedblock($newblock,$context);
			return $this->replaceBlock($template,$id,$newblock);
		}
		// suppression du bloc
		return $this->replaceBlock($template,$id);
		echo "broken method";
	}
	// traitement des foreach
	function & for_each(& $template,$path,$id,$context) {
		//echo "<p>treating foreach $path $id</p>";
		//$context.=$path;
		$value=$this->data->get($path);
		if (!is_array($value)) {
			// le chemin indiqué n'est pas un tableau, suppression du bloc
			return $this->replaceBlock($template,$id);
		}
		// recuperation du bloc
		$block=$this->getBlock($template,$id);// echo affvar($block);
		// construction du remplacement
		$newblock='';
		foreach (array_keys($value) as $key) {
			// clonage du bloc (feedblock travaille par référence)
			$tofeed=$block;
			// traitement du sous bloc et ajout de l'iteration
			$newblock.=$this->feedBlock($tofeed,$context.$path.'['.$key.']');
		}
		//echo $newblock;
		// substitution
		return $this->replaceBlock($template,$id,$newblock);
	}
	// traitement des for
	function & for_in(& $template,$start,$end,$increment,$id,$context) {
		$n=0; settype($start,'float'); settype($end,'float'); settype($increment,'float');
		//trigger_error("treating for($start,$end,$increment)#$context");
		// recup du bloc a repeter
		$block=$this->getBlock($template,$id); $newblock='';
		if ($start<=$end and $increment>0) {
			for ($f=$start; $f<=$end; $f+=$increment) {
				// enregistrement de l'increment pour utilisation dans le template
				//echo strval($f).'<br />';
				$this->data->set('[template]['.substr($id,1).']',strval($f));
				// clonage du bloc, traitement et ajout de l'iteration
				$tofeed=$block;
				$newblock.=$this->feedBlock($tofeed,$context);
			}
		} else if ($start>=$end and $increment<0) {
			for ($f=$start; $f>=$end; $f+=$increment) {
				// enregistrement de l'increment pour utilisation dans le template
				$this->data->set('[template]['.substr($id,1).']',strval($f));
				// clonage du bloc, traitement et ajout de l'iteration
				$tofeed=$block;
				$newblock.=$this->feedBlock($tofeed,$context);
			}
		} else {
			return false;
		}
		// substitution
		return $this->replaceBlock($template,$id,$newblock);
	}

/*-------------------------------------------------------------------------------------------------
                                                                                           routines
-------------------------------------------------------------------------------------------------*/
	// remplace un bloc (commande+contenu+fermeture)
	function & replaceBlock(& $template,$id,$replacement='') {
		//$command=$this->escapeEregChars($command);
		if (!ereg('__end'.$id.'__',$template)) {
			trigger_error('commande '.$id.' non fermée',E_USER_NOTICE);
			return false;
			}
		$ereg='__'.$this->ereg_command.$id.'__.*__end'.$id.'__';// echo $ereg;
		return $template=ereg_replace($ereg,$replacement,$template);
	}
	function & getBlock(&$template,$id) {
		$ereg='__'.$this->ereg_command.$id.'__(.*)__end'.$id.'__';
		if (!ereg($ereg,$template,$found)) return false;
		//echo "<p>getting block $id ".affvar($found)."<p>";
		return $found[2];
	}
	function & removeBlockCommand(& $template,$id) {
		$ereg_command='__'.$this->ereg_command.'('.$this->ereg_id.')?__';
		if (!$template=ereg_replace($ereg_command,'',$template)) return false;
		if (!ereg('__end'.$id.'__',$template)) return true;
		else return $template=ereg_replace('__end'.$id.'__','',$template);
	}
	function escapeEregChars($path) {
		$path=str_replace('[','\[',$path);
		$path=str_replace(']','\]',$path);
		$path=str_replace('(','\(',$path);
		$path=str_replace(')','\)',$path);
		//$path=str_replace('\\','\\\\',$path);
		return $path;
	}
	// renvoie la valeur textuelle d'une operande, valeur du chemin, constante ou (valeur)
	function getStrVal($operand) {
		//echo $operand;
		switch (true) {
			case ereg('^\((.*)\)$',$operand,$found):
				return $found[1];
			case ereg($this->ereg_path,$operand):
				$value=$this->data->get($operand);// echo affvar($value)."=>".strval($value);
				return strval($value);
			case (ereg('^'.$this->ereg_constant.'$',$operand) and defined($operand)):
				return eval(" return $operand;");
			default: return '';
		}
	}
}
?>
